/* Copyright (c) 2010, Carl Burch. License information is located in the
 * com.cburch.logisim.Main source code and at www.cburch.com/logisim/. */

package com.cburch.logisim.circuit;

class SimulatorTicker extends Thread {
	private Simulator.PropagationManager manager;
	private int ticksPerTickPhase;
	private int millisPerTickPhase;

	private boolean shouldTick;
	private int ticksPending;
	private boolean complete;

	public SimulatorTicker(Simulator.PropagationManager manager) {
		this.manager = manager;
		ticksPerTickPhase = 1;
		millisPerTickPhase = 1000;
		shouldTick = false;
		ticksPending = 0;
		complete = false;
	}

	@Override
	public void run() {
		long lastTick = System.currentTimeMillis();
		while (true) {
			boolean curShouldTick = shouldTick;
			int millis = millisPerTickPhase;
			int ticks = ticksPerTickPhase;
			try {
				synchronized (this) {
					curShouldTick = shouldTick;
					millis = millisPerTickPhase;
					ticks = ticksPerTickPhase;
					while (!curShouldTick && ticksPending == 0 && !complete) {
						wait();
						curShouldTick = shouldTick;
						millis = millisPerTickPhase;
						ticks = ticksPerTickPhase;
					}
				}
			} catch (InterruptedException e) {
			}

			if (complete)
				break;

			int toTick;
			long now = System.currentTimeMillis();
			if (curShouldTick && now - lastTick >= millis) {
				toTick = ticks;
			} else {
				toTick = ticksPending;
			}

			if (toTick > 0) {
				lastTick = now;
				for (int i = 0; i < toTick; i++) {
					manager.requestTick();
				}
				synchronized (this) {
					if (ticksPending > toTick)
						ticksPending -= toTick;
					else
						ticksPending = 0;
				}
				// we fire tickCompleted in this thread so that other
				// objects (in particular the repaint process) can slow
				// the thread down.
			}

			try {
				long nextTick = lastTick + millis;
				int wait = (int) (nextTick - System.currentTimeMillis());
				if (wait < 1)
					wait = 0;
				Thread.sleep(wait / 2); // this is shit and this way can go like 4KHz don't know why
			} catch (InterruptedException e) {
			}
		}
	}

	synchronized void setAwake(boolean value) {
		shouldTick = value;
		if (shouldTick)
			notifyAll();
	}

	public synchronized void setTickFrequency(int millis, int ticks) {
		millisPerTickPhase = millis;
		ticksPerTickPhase = ticks;
	}

	public synchronized void shutDown() {
		complete = true;
		notifyAll();
	}

	public synchronized void tickOnce() {
		ticksPending++;
		notifyAll();
	}
}